/*
 * Copyright (c) 2015, EURECOM (www.eurecom.fr)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the FreeBSD Project.
 */

#include <string.h>
#include <unistd.h>

#define G_LOG_DOMAIN ("PARSER")

#include <gtk/gtk.h>

#include <libxml/parser.h>
#include <libxml/tree.h>

#include "logs.h"
#include "types.h"
#include "union_type.h"
#include "xml_parse.h"

#include "ui_filters.h"
#include "ui_interface.h"
#include "ui_main_screen.h"
#include "ui_notif_dlg.h"

#include "../libresolver/locate_root.h"
#include "../libresolver/resolvers.h"

extern int debug_parser;

#if (ENABLE_DISPLAY_PARSE_INFO != 0)
#define INDENT_START 30
#else
#define INDENT_START 0
#endif

void *xml_raw_data;
uint32_t xml_raw_data_size;

types_t *xml_head;
types_t *root;

static int xml_parse_doc(xmlDocPtr doc);

static int parse_attribute_name(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"name")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve name attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      return -1;
    } else {
      g_debug("cannot retrieve name attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      return 0;
    }
  }

  type->name = strdup((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_id(xmlNode *node, types_t *type) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"id")) == NULL) {
    g_error("cannot retrieve id attribute in node %s, %s:%ld",
            (char *)node->name, node->doc->URL, XML_GET_LINE(node));
    return -1;
  }

  type->id = atoi((char *)&node_attribute->children->content[1]);
  return 0;
}

static int parse_attribute_size(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"size")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve size attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->size = -1;
      return -1;
    } else {
      g_debug("cannot retrieve size attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->size = -1;
      return 0;
    }
  }

  type->size = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_align(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"align")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve align attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->align = -1;
      return -1;
    } else {
      g_debug("cannot retrieve align attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->align = -1;
      return 0;
    }
  }

  type->align = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_line(xmlNode *node, types_t *type) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"line")) == NULL) {
    type->line = -1;
    return 0;
  }

  type->line = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_file(xmlNode *node, types_t *type) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"file")) == NULL) {
    type->file = NULL;
    return 0;
  }

  type->file = strdup((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_context(xmlNode *node, types_t *type,
                                   int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"context")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve context attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->context = -1;
      return -1;
    } else {
      type->context = -1;
      return 0;
    }
  }

  type->context = atoi((char *)&node_attribute->children->content[1]);
  return 0;
}

static int parse_attribute_artificial(xmlNode *node, types_t *type) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"artificial")) == NULL) {
    g_debug("cannot retrieve artificial attribute in node %s, %s:%ld",
            (char *)node->name, node->doc->URL, XML_GET_LINE(node));
    type->artificial = -1;
    return 0;
  }

  type->artificial = atoi((char *)&node_attribute->children->content[1]);
  return 0;
}

static int parse_attribute_init(xmlNode *node, types_t *type) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"init")) == NULL) {
    g_error("cannot retrieve init attribute in node %s, %s:%ld",
            (char *)node->name, node->doc->URL, XML_GET_LINE(node));
    type->init_value = -1;
    return 0;
  }

  type->init_value = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_members(xmlNode *node, types_t *type,
                                   int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"members")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve members attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->members = 0;
      return -1;
    } else {
      g_debug("cannot retrieve members attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->members = 0;
      return 0;
    }
  }

  type->members = strdup((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_mangled(xmlNode *node, types_t *type,
                                   int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"mangled")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve mangled attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->mangled = 0;
      return -1;
    } else {
      g_debug("cannot retrieve mangled attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->mangled = 0;
      return 0;
    }
  }

  type->mangled = strdup((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_demangled(xmlNode *node, types_t *type,
                                     int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"mangled")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve demangled attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->demangled = 0;
      return -1;
    } else {
      g_debug("cannot retrieve demangled attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->demangled = 0;
      return 0;
    }
  }

  type->demangled = strdup((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_offset(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"offset")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve offset attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->offset = -1;
      return -1;
    } else {
      g_debug("cannot retrieve offset attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->offset = -1;
      return 0;
    }
  }

  type->offset = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_bits(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"bits")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve bits attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->bits = -1;
      return -1;
    } else {
      g_debug("cannot retrieve bits attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->bits = -1;
      return 0;
    }
  }

  type->bits = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_type(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"type")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve type attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->type_xml = 0;
      return -1;
    } else {
      g_debug("cannot retrieve type attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->type_xml = 0;
      return 0;
    }
  }

  type->type_xml = atoi((char *)&node_attribute->children->content[1]);
  return 0;
}

static int parse_attribute_min(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"min")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve min attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->min = 0;
      return -1;
    } else {
      g_debug("cannot retrieve min attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->min = 0;
      return 0;
    }
  }

  type->min = atoi((char *)node_attribute->children->content);
  return 0;
}

static int parse_attribute_max(xmlNode *node, types_t *type, int mandatory) {
  xmlAttrPtr node_attribute;

  if ((node_attribute = xmlHasProp(node, (xmlChar *)"max")) == NULL) {
    if (mandatory) {
      g_error("cannot retrieve max attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->max = 0;
      return -1;
    } else {
      g_debug("cannot retrieve max attribute in node %s, %s:%ld",
              (char *)node->name, node->doc->URL, XML_GET_LINE(node));
      type->max = 0;
      return 0;
    }
  }

  type->max = atoi((char *)&node_attribute->children->content[1]);
  return 0;
}

static int parse_enum_values(xmlNode *node, types_t *parent) {
  types_t *new;

  if (node == NULL || parent == NULL) return -1;

  new = type_new(TYPE_ENUMERATION_VALUE);
  CHECK_FCT(parse_attribute_name(node, new, 1));
  CHECK_FCT(parse_attribute_init(node, new));
  CHECK_FCT(types_insert_tail(&parent->child, new));
  return 0;
}

static int parse_enumeration(xmlNode *node, types_t **head) {
  types_t *new;
  xmlNode *enum_value_node;

  if (node == NULL) return -1;

  new = type_new(TYPE_ENUMERATION);
  CHECK_FCT(parse_attribute_name(node, new, 1));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_size(node, new, 1));
  CHECK_FCT(parse_attribute_align(node, new, 1));
  CHECK_FCT(parse_attribute_context(node, new, 0));
  CHECK_FCT(parse_attribute_line(node, new));
  CHECK_FCT(parse_attribute_file(node, new));
  CHECK_FCT(parse_attribute_artificial(node, new));
  CHECK_FCT(types_insert_tail(head, new));

  /*
   * Parse enum values
   */
  for (enum_value_node = node->children; enum_value_node;
       enum_value_node = enum_value_node->next) {
    if (strcmp((char *)enum_value_node->name, "EnumValue") == 0)
      CHECK_FCT(parse_enum_values(enum_value_node, new));
  }

  return 0;
}

static int parse_union(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_UNION);
  CHECK_FCT(parse_attribute_name(node, new, 0));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_size(node, new, 1));
  CHECK_FCT(parse_attribute_align(node, new, 1));
  CHECK_FCT(parse_attribute_context(node, new, 0));
  CHECK_FCT(parse_attribute_members(node, new, 0));
  CHECK_FCT(parse_attribute_line(node, new));
  CHECK_FCT(parse_attribute_file(node, new));
  CHECK_FCT(parse_attribute_artificial(node, new));
  CHECK_FCT(parse_attribute_mangled(node, new, 0));
  CHECK_FCT(parse_attribute_demangled(node, new, 0));

  if (new->name)
    if (strcmp(new->name, "msg_s") == 0)
      new->type_dissect_from_buffer = union_msg_dissect_from_buffer;

  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_fundamental(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_FUNDAMENTAL);
  CHECK_FCT(parse_attribute_name(node, new, 1));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_size(node, new, 0));
  CHECK_FCT(parse_attribute_align(node, new, 0));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_struct(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_STRUCT);
  CHECK_FCT(parse_attribute_name(node, new, 0));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_size(node, new, 0));
  CHECK_FCT(parse_attribute_align(node, new, 0));
  CHECK_FCT(parse_attribute_file(node, new));
  CHECK_FCT(parse_attribute_line(node, new));
  CHECK_FCT(parse_attribute_context(node, new, 0));
  CHECK_FCT(parse_attribute_artificial(node, new));
  CHECK_FCT(parse_attribute_members(node, new, 0));
  CHECK_FCT(parse_attribute_mangled(node, new, 0));
  CHECK_FCT(parse_attribute_demangled(node, new, 0));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_typedef(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_TYPEDEF);
  CHECK_FCT(parse_attribute_name(node, new, 1));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_file(node, new));
  CHECK_FCT(parse_attribute_line(node, new));
  CHECK_FCT(parse_attribute_type(node, new, 1));
  CHECK_FCT(parse_attribute_size(node, new, 0));
  CHECK_FCT(parse_attribute_align(node, new, 0));
  CHECK_FCT(parse_attribute_context(node, new, 0));
  CHECK_FCT(parse_attribute_artificial(node, new));
  CHECK_FCT(parse_attribute_members(node, new, 0));
  CHECK_FCT(parse_attribute_mangled(node, new, 0));
  CHECK_FCT(parse_attribute_demangled(node, new, 0));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_field(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_FIELD);
  CHECK_FCT(parse_attribute_name(node, new, 1));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_type(node, new, 1));
  CHECK_FCT(parse_attribute_size(node, new, 0));
  CHECK_FCT(parse_attribute_bits(node, new, 0));
  CHECK_FCT(parse_attribute_offset(node, new, 0));
  CHECK_FCT(parse_attribute_align(node, new, 0));
  CHECK_FCT(parse_attribute_context(node, new, 0));
  CHECK_FCT(parse_attribute_artificial(node, new));
  CHECK_FCT(parse_attribute_members(node, new, 0));
  CHECK_FCT(parse_attribute_mangled(node, new, 0));
  CHECK_FCT(parse_attribute_demangled(node, new, 0));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_file(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_FILE);
  CHECK_FCT(parse_attribute_name(node, new, 1));
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_reference_type(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_REFERENCE);
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_type(node, new, 1));
  CHECK_FCT(parse_attribute_size(node, new, 1));
  CHECK_FCT(parse_attribute_align(node, new, 1));
  CHECK_FCT(parse_attribute_context(node, new, 0));
  CHECK_FCT(parse_attribute_offset(node, new, 0));
  CHECK_FCT(parse_attribute_file(node, new));
  CHECK_FCT(parse_attribute_line(node, new));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_array_type(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_ARRAY);
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_type(node, new, 1));
  CHECK_FCT(parse_attribute_size(node, new, 0));
  CHECK_FCT(parse_attribute_align(node, new, 1));
  CHECK_FCT(parse_attribute_min(node, new, 1));
  CHECK_FCT(parse_attribute_max(node, new, 1));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_pointer_type(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_POINTER);
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(parse_attribute_type(node, new, 1));
  CHECK_FCT(parse_attribute_size(node, new, 1));
  CHECK_FCT(parse_attribute_align(node, new, 1));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

static int parse_function_type(xmlNode *node, types_t **head) {
  types_t *new;

  if (node == NULL) return -1;

  new = type_new(TYPE_FUNCTION);
  CHECK_FCT(parse_attribute_id(node, new));
  CHECK_FCT(types_insert_tail(head, new));
  return 0;
}

/**
   print_element_names:
   @a_node: the initial xml node to consider.

   Prints the names of the all the xml elements
   that are siblings or children of a given xml node.
*/
static int parse_elements(xmlNode *a_node, types_t **head) {
  xmlNode *cur_node = NULL;
  xmlNode *child_node = NULL;

  for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
    for (child_node = cur_node->children; child_node;
         child_node = child_node->next) {
      if (child_node->type == XML_ELEMENT_NODE) {
        if (strcmp((char *)child_node->name, "Enumeration") == 0) {
          CHECK_FCT_DO(parse_enumeration(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "FundamentalType") == 0) {
          CHECK_FCT_DO(parse_fundamental(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "Struct") == 0) {
          CHECK_FCT_DO(parse_struct(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "Union") == 0) {
          CHECK_FCT_DO(parse_union(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "Typedef") == 0) {
          CHECK_FCT_DO(parse_typedef(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "File") == 0) {
          CHECK_FCT_DO(parse_file(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "Field") == 0) {
          CHECK_FCT_DO(parse_field(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "ReferenceType") == 0) {
          CHECK_FCT_DO(parse_reference_type(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "ArrayType") == 0) {
          CHECK_FCT_DO(parse_array_type(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "PointerType") == 0) {
          CHECK_FCT_DO(parse_pointer_type(child_node, head), return RC_FAIL);
        } else if (strcmp((char *)child_node->name, "FunctionType") == 0) {
          CHECK_FCT_DO(parse_function_type(child_node, head), return RC_FAIL);
        }
      }
    }
  }

  return RC_OK;
}

static int free_elements(types_t *parent, int indent) {
  types_t *cur_node = parent;
  types_t *child_node;

  g_debug("%*s%p %s", indent, "", cur_node,
          cur_node->name != NULL ? cur_node->name : "");

  for (; cur_node != NULL; cur_node = cur_node->next) {
    if (cur_node->child != NULL) {
      child_node = cur_node->child;
      cur_node->child = NULL; /* Clear the child pointer to avoid re-processing
                                 it, we are handling a graph with loops */
      CHECK_FCT_DO(free_elements((child_node), indent + 2), return RC_FAIL);
    }
  }

  free(cur_node);
  return RC_OK;
}

int xml_parse_buffer(char *xml_buffer, const int size) {
  xmlDocPtr doc; /* the resulting document tree */

  if (xml_buffer == NULL) {
    return RC_NULL_POINTER;
  }

  if (xml_raw_data != NULL) {
    /*
     * Free previous raw data
     */
    free(xml_raw_data);
  }

  xml_raw_data = xml_buffer;
  xml_raw_data_size = size;

  if (xml_head != NULL) {
    /*
     * Free previous definitions
     */
    free_elements(xml_head, 0);
    g_info("xml_parse_buffer freed previous definitions");
  }

  xml_head = NULL;
  root = NULL;
  messages_id_enum = NULL;
  lte_time_type = NULL;
  lte_time_frame_type = NULL;
  lte_time_slot_type = NULL;
  origin_task_id_type = NULL;
  destination_task_id_type = NULL;
  instance_type = NULL;
  message_header_type = NULL;
  message_type = NULL;
  message_size_type = NULL;
  g_info("Parsing XML definition from buffer ...");
  doc = xmlReadMemory(xml_buffer, size, NULL, NULL, 0);

  if (doc == NULL) {
    g_warning("Failed to parse XML buffer: %s", xml_buffer);
    ui_notification_dialog(GTK_MESSAGE_ERROR, FALSE,
                           "parse messages format definition",
                           "Fail to parse XML buffer");
    return RC_FAIL;
  }

  return xml_parse_doc(doc);
}

#if 0 /* Not used anymore */
int
xml_parse_file (
  const char *filename)
{
  xmlDocPtr                               doc;  /* the resulting document tree */

  if (filename == NULL) {
    return RC_NULL_POINTER;
  }

  doc = xmlReadFile (filename, NULL, 0);

  if (doc == NULL) {
    ui_notification_dialog (GTK_MESSAGE_ERROR, FALSE, "parse messages format definition", "Failed to parse file \"%s\"", filename);
    return RC_FAIL;
  }

  return xml_parse_doc (doc);
}
#endif

static int update_filters() {
  types_t *types;

  ui_init_filters(FALSE, TRUE);
  types = messages_id_enum;

  if (types != NULL) {
    types = types->child;

    while (types != NULL) {
      if (strcmp(types->name, "MESSAGES_ID_MAX") != 0) {
        ui_filters_add(FILTER_MESSAGES, types->init_value, types->name,
                       ENTRY_ENABLED_UNDEFINED, NULL, NULL);
      }

      types = types->next;
    }
  }

  types = origin_task_id_type;

  if (types != NULL) {
    types = types->child->child;

    while (types != NULL) {
      if ((strcmp(types->name, "TASK_FIRST") != 0) &&
          (strcmp(types->name, "TASK_MAX") != 0)) {
        ui_filters_add(FILTER_ORIGIN_TASKS, types->init_value, types->name,
                       ENTRY_ENABLED_UNDEFINED, NULL, NULL);
      }

      types = types->next;
    }
  }

  types = destination_task_id_type;

  if (types != NULL) {
    types = types->child->child;

    while (types != NULL) {
      if ((strcmp(types->name, "TASK_FIRST") != 0) &&
          (strcmp(types->name, "TASK_MAX") != 0)) {
        ui_filters_add(FILTER_DESTINATION_TASKS, types->init_value, types->name,
                       ENTRY_ENABLED_UNDEFINED, NULL, NULL);
      }

      types = types->next;
    }
  }

  return RC_OK;
}

static int xml_parse_doc(xmlDocPtr doc) {
  xmlNode *root_element = NULL;
  int ret = 0;
  FILE *dissect_file = NULL;

  if (ui_main_data.dissect_file_name != NULL) {
    dissect_file = fopen(ui_main_data.dissect_file_name, "w");
  }

  /*
   * Get the root element node
   */
  root_element = xmlDocGetRootElement(doc);
  ret = parse_elements(root_element, &xml_head);
  /*
   * Free the document
   */
  xmlFreeDoc(doc);

  if (ret == RC_OK) {
    resolve_typedefs(&xml_head);
    resolve_pointer_type(&xml_head);
    resolve_field(&xml_head);
    resolve_array(&xml_head);
    resolve_reference(&xml_head);
    resolve_struct(&xml_head);
    resolve_file(&xml_head);
    resolve_union(&xml_head);
    resolve_function(&xml_head);
    /*
     * Locate the root element which corresponds to the MessageDef struct
     */
    CHECK_FCT(locate_root("MessageDef", xml_head, &root));

    /*
     * Locate the LTE time fields
     */
    if (locate_type("lte_time", xml_head, &lte_time_type) == RC_OK) {
      CHECK_FCT(locate_type("frame", lte_time_type->child->child,
                            &lte_time_frame_type));
      CHECK_FCT(locate_type("slot", lte_time_type->child->child,
                            &lte_time_slot_type));
    }

    /*
     * Locate the message id field
     */
    CHECK_FCT(locate_type("MessagesIds", xml_head, &messages_id_enum));
    /*
     * Locate the header part of a message
     */
    CHECK_FCT(locate_type("ittiMsgHeader", xml_head, &message_header_type));
    /*
     * Locate the main message part
     */
    CHECK_FCT(locate_type("ittiMsg", xml_head, &message_type));
    /*
     * Locate the origin task id field
     */
    CHECK_FCT(locate_type_children("originTaskId",
                                   message_header_type->child->child,
                                   &origin_task_id_type));
    /*
     * Locate the destination task id field
     */
    CHECK_FCT(locate_type_children("destinationTaskId",
                                   message_header_type->child->child,
                                   &destination_task_id_type));
    /*
     * Locate the instance field
     */
    CHECK_FCT(locate_type_children(
        "instance", message_header_type->child->child, &instance_type));
    /*
     * Locate the message size field
     */
    CHECK_FCT(locate_type_children(
        "ittiMsgSize", message_header_type->child->child, &message_size_type));
    // root->type_hr_display(root, 0);
    update_filters();

    if (dissect_file != NULL) {
      g_debug("generating dissected types file \"%s\" ...",
              ui_main_data.dissect_file_name);
      root->type_file_print(root, 0, dissect_file);
    }
  }

  if (dissect_file != NULL) {
    fclose(dissect_file);
  }

  g_message("Parsed XML definition");
  return ret;
}

int dissect_signal_header(buffer_t *buffer,
                          ui_set_signal_text_cb_t ui_set_signal_text_cb,
                          gpointer cb_user_data) {
  if (message_header_type == NULL) {
    g_error("No messages format definition provided");
    return RC_FAIL;
  }

  if (buffer == NULL) {
    g_error("Failed buffer is NULL");
    return RC_FAIL;
  }

  message_header_type->type_dissect_from_buffer(
      message_header_type, ui_set_signal_text_cb, cb_user_data, buffer, 0, 0,
      INDENT_START, TRUE);
  return RC_OK;
}

int dissect_signal(buffer_t *buffer,
                   ui_set_signal_text_cb_t ui_set_signal_text_cb,
                   gpointer cb_user_data) {
  if (message_type == NULL) {
    g_error("No messages format definition provided");
    return RC_FAIL;
  }

  if (buffer == NULL) {
    g_error("Failed buffer is NULL");
    return RC_FAIL;
  }

  message_type->type_dissect_from_buffer(message_type, ui_set_signal_text_cb,
                                         cb_user_data, buffer, 0, 0,
                                         INDENT_START, TRUE);
  return RC_OK;
}
